package cn.lili.modules.kit.plugin.wallet;

import cn.lili.common.enums.ClientTypeEnum;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.event.TransactionCommitSendMQEvent;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.security.context.UserContext;
import cn.lili.common.security.enums.SceneEnums;
import cn.lili.common.utils.CurrencyUtil;
import cn.lili.common.utils.ResultUtil;
import cn.lili.common.utils.SnowFlake;
import cn.lili.common.utils.StringUtils;
import cn.lili.common.vo.ResultMessage;
import cn.lili.exchange.AmqpExchangeProperties;
import cn.lili.modules.distribution.entity.dos.Distribution;
import cn.lili.modules.goods.entity.dos.GoodsSku;
import cn.lili.modules.kit.Payment;
import cn.lili.modules.member.entity.dos.User;
import cn.lili.modules.order.order.client.OrderClient;
import cn.lili.modules.order.order.entity.dos.OrderItem;
import cn.lili.modules.order.order.entity.dos.RankingActivityConfig;
import cn.lili.modules.order.order.entity.dos.RankingScoreDetails;
import cn.lili.modules.payment.entity.dos.*;
import cn.lili.modules.payment.entity.dto.*;
import cn.lili.modules.payment.entity.enums.OutOrderLogStatusEnums;
import cn.lili.modules.payment.entity.enums.PaymentMethodEnum;
import cn.lili.modules.payment.entity.enums.PlatformWalletEnum;
import cn.lili.modules.payment.entity.enums.WalletServiceTypeEnum;
import cn.lili.modules.payment.entity.vo.OutOrderLogVO;
import cn.lili.modules.payment.entity.vo.WalletVO;
import cn.lili.modules.payment.service.*;
import cn.lili.modules.system.constants.DictConstant;
import cn.lili.modules.system.entity.enums.SettingEnum;
import cn.lili.modules.wallet.service.WalletService;
import cn.lili.routing.PaymentRoutingKey;
import com.alibaba.fastjson.JSONObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * WalletPlugin
 *
 * @author Chopper
 * @version v1.0 2021-02-20 10:14
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class WalletPlugin implements Payment {


    private final ApplicationEventPublisher applicationEventPublisher;

    private final AmqpExchangeProperties amqpExchangeProperties;

    private final WalletService walletService;


    private final PaymentLogService paymentLogService;

    private final OutOrderLogService outOrderLogService;

    private final OutOrderDetailLogService outOrderDetailLogService;

    private final PaymentSubsidiesService paymentSubsidiesService;

    private final PaymentUrbanSubstationCommissionService paymentUrbanSubstationCommissionService;

    private final OrderClient orderClient;

    @Override
    public ResultMessage<Object> h5pay(PaymentWakeupParam paymentWakeupParam) {

        return exec(paymentWakeupParam);
    }

    @Override
    public ResultMessage<Object> jsApiPay(PaymentWakeupParam paymentWakeupParam) {

        return exec(paymentWakeupParam);
    }

    @Override
    public ResultMessage<Object> appPay(PaymentWakeupParam paymentWakeupParam) {

        return exec(paymentWakeupParam);
    }

    @Override
    public ResultMessage<Object> nativePay(PaymentWakeupParam paymentWakeupParam) {

        return exec(paymentWakeupParam);
    }

    @Override
    public ResultMessage<Object> mpPay(PaymentWakeupParam paymentWakeupParam) {

        return exec(paymentWakeupParam);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PaymentSubsidies subsidies(SubsidiesParams subsidiesParams) {

        WalletVO walletVO = walletService.getWalletVO(subsidiesParams.getPayerId());

        //如果订单金额大于钱包金额，直接返回 .当id为-1时，代表平台付款，不需要判断余额
        if (!subsidiesParams.getPayerId().equals("-1") && walletVO.getBalance() < subsidiesParams.getPrice()) {
            throw new ServiceException(ResultCode.WALLET_INSUFFICIENT);
        }


        //支付发起交易号
        try {
            PaymentSubsidies paymentSubsidies = PaymentSubsidies.builder()
                    //根据订单号，生成规律的补差流水号
                    .orderSn(subsidiesParams.getOrderSn())
                    .nickname(subsidiesParams.getNickname())
                    .payerId(subsidiesParams.getPayerId())
                    .description(PaymentSubsidies.generateSubsidiesText(subsidiesParams.getOrderSn(),
                            subsidiesParams.getPrice()))
                    .paymentClient(ClientTypeEnum.PC.name())
                    .paymentMethod(PaymentMethodEnum.WALLET.name())
                    .price(subsidiesParams.getPrice())
                    .payeeId("-1")
                    .createTime(new Date())
                    .outTradeNo(SnowFlake.getIdStr())
                    .transactionId(SnowFlake.getIdStr())
                    .build();


            //支付成功后，扣除钱包金额
            walletService.reduce(
                    UserWalletUpdateDTO.builder()
                            .userId(subsidiesParams.getPayerId())
                            .orderSn(subsidiesParams.getOrderSn())
                            .amount(subsidiesParams.getPrice())
                            .detail("订单[" + paymentSubsidies.getOrderSn() + "]补差,金额[" + paymentSubsidies.getPrice() + "]")
                            .transactionId(paymentSubsidies.getTransactionId())
                            .outTradeNo(paymentSubsidies.getOutTradeNo())
                            .serviceType(WalletServiceTypeEnum.PAY)
                            .platformWalletId(PlatformWalletEnum.BALANCE.getWalletUserId())
                            .build());

            PaymentCallback paymentCallback =
                    PaymentCallback.builder()
                            .paymentSubsidies(paymentSubsidies)
                            .isCombine(false)
                            .build();

            applicationEventPublisher.publishEvent(
                    TransactionCommitSendMQEvent.builder()
                            .source("补差支付回调")
                            .exchange(amqpExchangeProperties.getPayment())
                            .routingKey(PaymentRoutingKey.SUBSIDIES_CALLBACK)
                            .message(paymentCallback)
                            .build()
            );
            paymentSubsidiesService.save(paymentSubsidies);
            return paymentSubsidies;
        } catch (Exception e) {
            log.error("补差支付失败", e);
            throw new ServiceException(ResultCode.PAY_ERROR);
        }


    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OutOrderLogVO profitSharing(ProfitSharingRequestDTO profitSharingRequestDTO) {
        //获取支付日志
        PaymentLog paymentLog =
                paymentLogService.getPaymentLogByOrderSN(profitSharingRequestDTO.getOrderSn());
        if (paymentLog == null) {
            orderClient.updateProfitSharingOrderFlow(profitSharingRequestDTO.getOrderSn());
            orderClient.addOrderFlowProfitSharing(profitSharingRequestDTO.getOrderSn(),"支付日志不存在");
            throw new ServiceException(ResultCode.PROFIT_SHARING_ERROR, "支付日志不存在");
        }

        OutOrderLog outOrderLog = outOrderLogService.getByOrderSn(profitSharingRequestDTO.getOrderSn());
        if (null == outOrderLog) {
            outOrderLog = OutOrderLog.builder().paymentMethod(profitSharingRequestDTO.getPaymentMethodEnum().name())
                    .orderSn(profitSharingRequestDTO.getOrderSn())
                    .amount(profitSharingRequestDTO.getTotalAmount())
                    .paymentMethod(PaymentMethodEnum.WALLET.name())
                    .status(OutOrderLogStatusEnums.FINISHED.name())
                    .outOrderNo(SnowFlake.createStr("OOL"))
                    .build();

            outOrderLogService.save(outOrderLog);
        }

        //保存分账子单记录
        List<OutOrderDetailLog> outOrderDetailLogs = new ArrayList<>();
        for (OutOrderItem outOrderItem : profitSharingRequestDTO.getOutOrderItems()) {
            OutOrderDetailLog outOrderDetailLog = new OutOrderDetailLog(profitSharingRequestDTO.getOrderSn(), outOrderLog.getOutOrderNo(),
                    outOrderItem);
            outOrderDetailLogs.add(outOrderDetailLog);
        }

        //保存分账子账单信息
        outOrderDetailLogService.saveBatch(outOrderDetailLogs);

        walletService.profitSharing(profitSharingRequestDTO);

        return new OutOrderLogVO(outOrderLog, outOrderDetailLogs);
    }

    @Override
    @Transactional
    public void profitSharingSubsidies(OutOrderLog outOrderLog) {

        List<PaymentSubsidies> paymentSubsidies = paymentSubsidiesService.listByOrderSn(outOrderLog.getOrderSn());

        if (paymentSubsidies.isEmpty()) {
            return;
        }

        //将出账单号更新近日志信息
        paymentSubsidies.forEach(item -> {
            item.setOutOrderNo(outOrderLog.getOutOrderNo());
        });
        paymentSubsidiesService.updateBatchById(paymentSubsidies);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OutOrderLogVO specialProfitSharing(OutOrderLog outOrderLog, ProfitSharingRequestDTO profitSharingRequestDTO) {

        //保存分账子单记录
        List<OutOrderDetailLog> outOrderDetailLogs = new ArrayList<>();
        for (OutOrderItem outOrderItem : profitSharingRequestDTO.getOutOrderItems()) {
            //只对有金额的子单进行分账 小于0的在下单审批过程中已经通过钱包补交了。
            if (outOrderItem.getPrice() > 0) {
                OutOrderDetailLog outOrderDetailLog = new OutOrderDetailLog(profitSharingRequestDTO.getOrderSn(), outOrderLog.getOutOrderNo(),
                        outOrderItem);
                outOrderDetailLogs.add(outOrderDetailLog);
            }
        }

        //保存分账子账单信息
        outOrderDetailLogService.saveBatch(outOrderDetailLogs);

        walletService.profitSharing(profitSharingRequestDTO);

        return new OutOrderLogVO(outOrderLog, outOrderDetailLogs);
    }

    @Override
    public OutOrderLog profitSharingPlatform(PaymentLog paymentLog) {
        OutOrderLog outOrderLog = outOrderLogService.getByOrderSn(paymentLog.getOrderSn());
        if (null == outOrderLog) {
            outOrderLog = OutOrderLog.builder().paymentMethod(paymentLog.getPaymentMethod())
                    .orderSn(paymentLog.getOrderSn())
                    .amount(paymentLog.getPrice())
                    .paymentMethod(PaymentMethodEnum.WALLET.name())
                    .status(OutOrderLogStatusEnums.FINISHED.name())
                    .outOrderNo(SnowFlake.createStr("OOL"))
                    .build();

            outOrderLogService.save(outOrderLog);
        }

        //保存分账子单记录
        OutOrderDetailLog outOrderDetailLog = OutOrderDetailLog.builder()
                .orderSn(paymentLog.getOrderSn())
                .outOrderNo(outOrderLog.getOutOrderNo())
                .amount(paymentLog.getPrice())
                .subMchid(PlatformWalletEnum.BALANCE.getWalletUserId())
                .result("SUCCESS")
                .subMchName(PlatformWalletEnum.BALANCE.getDescription())
                .description(OutOrderDetailLog.generateSubsidiesToPlatform(paymentLog.getOrderSn(), paymentLog.getPrice()))
                .build();
        //保存分账子账单信息
        outOrderDetailLogService.save(outOrderDetailLog);
        return outOrderLog;
    }

    /**
     * 保存支付日志
     *
     * @param paymentWakeupParam 支付参数
     */
    private ResultMessage<Object> exec(PaymentWakeupParam paymentWakeupParam) {

        WalletVO walletVO = walletService.getWalletVO(UserContext.getCurrentId());

        PayParam payParam = paymentWakeupParam.getPayParam();
        //如果订单金额大于钱包金额，直接返回
        if (walletVO.getBalance() < payParam.getTotalAmount()) {
            throw new ServiceException(ResultCode.WALLET_INSUFFICIENT);
        }

        //支付发起交易号
        try {
            if (Boolean.TRUE.equals(paymentWakeupParam.getIsCombine())) {
                CombinePaymentLog combinePaymentLog = paymentWakeupParam.getCombinePaymentLog();
                combinePaymentLog.setCombineOutTradeNo(SnowFlake.getIdStr());
                //标记已支付，且已校验
                combinePaymentLog.setIsCheck(true);

                paymentWakeupParam.getPaymentLogs().forEach(paymentLog -> {
                    paymentLog.setOutTradeNo(SnowFlake.getIdStr());
                    paymentLog.setTransactionId(SnowFlake.getIdStr());
                    paymentLog.setCombineOutTradeNo(combinePaymentLog.getCombineOutTradeNo());
                    //标记已支付，且已校验
                    paymentLog.setIsPay(true);
                    paymentLog.setIsCheck(true);
                });


                //扣除钱包金额
                walletService.reduce(
                        UserWalletUpdateDTO.builder()
                                .userId(UserContext.getCurrentId())
                                .orderSn(paymentWakeupParam.getCombinePaymentLog().getOrderSn())
                                .scene(UserContext.getCurrentUser().getScene())
                                .amount(payParam.getTotalAmount())
                                .detail("合并支付，订单号：[" + paymentWakeupParam.getPaymentLogs().stream().map(PaymentLog::getOrderSn).collect(Collectors.joining(", ")) + "]，支付金额[" + payParam.getTotalAmount() + "]")
                                .transactionId("合单支付")
                                .outTradeNo(combinePaymentLog.getCombineOutTradeNo())
                                .serviceType(WalletServiceTypeEnum.PAY)
                                .platformWalletId(PlatformWalletEnum.BALANCE.getWalletUserId())
                                .build());
            } else {

                PaymentLog paymentLog = paymentWakeupParam.getPaymentLog();

                paymentLog.setOutTradeNo(SnowFlake.getIdStr());
                paymentLog.setTransactionId(SnowFlake.getIdStr());
                //标记已支付，且已校验
                paymentLog.setIsPay(true);
                paymentLog.setIsCheck(true);


                //支付成功后，扣除钱包金额
                walletService.reduce(
                        UserWalletUpdateDTO.builder()
                                .userId(UserContext.getCurrentId())
                                .orderSn(paymentLog.getOrderSn())
                                .scene(UserContext.getCurrentUser().getScene())
                                .amount(payParam.getTotalAmount())
                                .detail("支付订单[" + paymentLog.getOrderSn() + "]金额[" + paymentLog.getPrice() + "]")
                                .transactionId(paymentLog.getTransactionId())
                                .outTradeNo(paymentLog.getOutTradeNo())
                                .platformWalletId(PlatformWalletEnum.BALANCE.getWalletUserId())
                                .serviceType(WalletServiceTypeEnum.PAY)
                                .build());
            }

            PaymentCallback paymentCallback =
                    Boolean.TRUE.equals(payParam.getIsCombine()) ?
                            PaymentCallback.builder()
                                    .paymentLogs(paymentWakeupParam.getPaymentLogs())
                                    .combinePaymentLog(paymentWakeupParam.getCombinePaymentLog())
                                    .isCombine(true)
                                    .build() :
                            PaymentCallback.builder()
                                    .paymentLog(paymentWakeupParam.getPaymentLog())
                                    .isCombine(false)
                                    .build();
            log.info("余额支付回调");
            applicationEventPublisher.publishEvent(
                    TransactionCommitSendMQEvent.builder()
                            .source("余额支付回调")
                            .exchange(amqpExchangeProperties.getPayment())
                            .routingKey(PaymentRoutingKey.PAYMENT_CALLBACK)
                            .message(paymentCallback)
                            .build()

            );
        } catch (Exception e) {
            log.error("余额支付失败", e);
            throw new ServiceException(ResultCode.PAY_ERROR);
        }


        return ResultUtil.success();
    }

    @Override
    public void refund(RefundLog refundLog) {
        PaymentLog paymentLog = paymentLogService.getPaymentLogByOutTradeNo(refundLog.getOutTradeNo());
        if (paymentLog == null) {
            refundLog.setErrorMessage("支付日志不存在，无法退款");
        } else {
            refundLog.setIsRefund(true);
            refundLog.setRefundTransactionId(SnowFlake.getIdStr());
            walletService.increase(
                    UserWalletUpdateDTO.builder()
                            .userId(refundLog.getUserId())
                            .amount(refundLog.getPrice())
                            .orderSn(refundLog.getOrderSn())
                            .detail("订单[" + refundLog.getOrderSn() + "]，售后单[" + refundLog.getAfterSaleNo() + "]，退还金额[" + refundLog.getPrice() + "]")
                            .outTradeNo(refundLog.getAfterSaleNo())
                            .platformWalletId(PlatformWalletEnum.BALANCE.getWalletUserId())
                            .transactionId(refundLog.getTransactionId())
                            .serviceType(WalletServiceTypeEnum.REFUND)
                            .build());

        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PaymentUrbanSubstationCommission urbanSubstationCommission(UrbanSubstationCommissionParams urbanSubstationCommissionParams) {
        //支付发起交易号
        try {
            PaymentUrbanSubstationCommission paymentUrbanSubstationCommission = PaymentUrbanSubstationCommission.builder()
                    .urbanId(urbanSubstationCommissionParams.getPayeeId())
                    .supplierId(urbanSubstationCommissionParams.getPaymentId())
                    .paymentClient(ClientTypeEnum.PC.name())
                    .paymentMethod(PaymentMethodEnum.WALLET.name())
                    .price(urbanSubstationCommissionParams.getPrice())
                    .status(1)
                    .createTime(new Date())
                    .build();


            //支付成功后，扣除钱包金额
            walletService.reduce(
                    UserWalletUpdateDTO.builder()
                            .userId(PlatformWalletEnum.BALANCE.getWalletUserId())
                            .amount(urbanSubstationCommissionParams.getPrice())
                            .detail("供应商[" + urbanSubstationCommissionParams.getPaymentId() + "]审核费用,城市分站金额[" + urbanSubstationCommissionParams.getPrice() + "]")
                            .serviceType(WalletServiceTypeEnum.PAY)
                            .platformWalletId(urbanSubstationCommissionParams.getPayeeId())
                            .build());

            paymentUrbanSubstationCommissionService.save(paymentUrbanSubstationCommission);
            return paymentUrbanSubstationCommission;
        } catch (Exception e) {
            log.error("补差支付失败", e);
            throw new ServiceException(ResultCode.PAY_ERROR);
        }


    }

}



